package com.ants.boot.utils.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Joiner;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 蚂蚁会花呗
 * @date 2022/10/24 10:55
 * HttpClient 请求工具类
 * 需要引入httpclient和httpmime包
 */
public class HttpClientUtils {

    private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);

    /**
     * 默认的上传文件时的参数名
     */
    private static final String DEFAULT_FILE_NAME = "file";


    /**
     * get请求，没有参数，或者参数已经在url地址上
     * @param url 带有参数的url或者纯url
     * @return 字符串结果
     */
    public static String sendGet(String url){
        return sendGetRequest(url,null);
    }

    /**
     * get 请求，url + map参数，最终请求格式为 http://xxxx.com?a=1&b=2
     * @param url 请求地址
     * @param params 参数
     * @return 字符串结果
     */
    public static String sendGet(String url,Map<String,Object> params){
        return sendGetRequest(url,params);
    }

    /**
     * get请求，没有参数，或者参数已经在url地址上
     * @param url 带有参数的url或者纯url
     * @return json对象，失败或者异常返回null
     */
    public static JSONObject sendGetGetObj(String url){
        return sendGetRequestGetObj(url,null);
    }
    /**
     * get 请求，url + map参数，最终请求格式为 http://xxxx.com?a=1&b=2
     * @param url 请求地址
     * @param params 参数
     * @return json对象，失败或者异常返回null
     */
    public static JSONObject sendGetGetObj(String url,Map<String,Object> params){
        return sendGetRequestGetObj(url,params);
    }


    /*******************************************返回字符串结果**************************************************/
    /**
     *
     * post formData 方式发送参数及文件
     * @param url post url
     * @param formData formData参数
     * @param headers 请求头
     * @return
     */
    public static String sendPostFormData(String url,Map<String,Object> headers, Map<String,Object> formData){
        return sendFormDataFiles(url,headers,formData,null);
    }
    /**
     * post formData 方式发送参数及文件，或者单独文件并使用默认文件名参数
     * @param url post请求地址
     * @param headers 请求头
     * @param formData 请求参数formData 方式
     * @param file 上传文件,使用默认的请求参数 file
     * @return
     */
    public static String sendPostFormData(String url,Map<String,Object> headers, Map<String,Object> formData,File file){
        if (file == null){
            throw new RuntimeException("file not is null!");
        }
        Map<String,File> files = new HashMap<>();
        files.put(DEFAULT_FILE_NAME,file);
        return sendFormDataFiles(url,headers,formData,files);
    }

    /**
     * post formData 方式发送参数及文件，或者单独文件并使用默认文件名参数
     * @param url post请求地址
     * @param headers 请求头
     * @param formData 请求参数formData 方式
     * @param file 上传文件
     * @param fileParamName 上传文件的参数名
     * @return
     */
    public static String sendPostFormData(String url,Map<String,Object> headers, Map<String,Object> formData,File file,String fileParamName){
        if (file == null){
            throw new RuntimeException("file not is null!");
        }
        Map<String,File> files = new HashMap<>();
        files.put(fileParamName,file);
        return sendFormDataFiles(url,headers,formData,files);
    }

    /**
     * post formData 方式发送参数及文件，或者单独文件并使用默认文件名参数
     * @param url post请求地址
     * @param headers 请求头
     * @param formData 请求参数formData 方式
     * @param files 文件，可以为多了，或者单个
     * @return
     */
    public static String sendPostFormData(String url,Map<String,Object> headers, Map<String,Object> formData,Map<String,File> files){
        return sendFormDataFiles(url,headers,formData,files);
    }


    /*******************************************返回JSONObject对象，为null 表示异常，正常返回code headers data******************************************/
    /**
     *
     * post formData 方式发送参数及文件
     * @param url post url
     * @param formData formData参数
     * @param headers 请求头
     * @return JSONObject 对象，包含headers code 和data
     */
    public static JSONObject sendPostFormDataGetObj(String url,Map<String,Object> headers, Map<String,Object> formData){
        return sendFormDataFilesGetObj(url,headers,formData,null);
    }
    /**
     * post formData 方式发送参数及文件，或者单独文件并使用默认文件名参数
     * @param url post请求地址
     * @param headers 请求头
     * @param formData 请求参数formData 方式
     * @param file 上传文件,使用默认的请求参数 file
     * @return JSONObject 对象，包含headers code 和data,异常时返回null
     */
    public static JSONObject sendPostFormDataGetObj(String url,Map<String,Object> headers, Map<String,Object> formData,File file){
        if (file == null){
            throw new RuntimeException("file not is null!");
        }
        Map<String,File> files = new HashMap<>();
        files.put(DEFAULT_FILE_NAME,file);
        return sendFormDataFilesGetObj(url,headers,formData,files);
    }

    /**
     * post formData 方式发送参数及文件，或者单独文件并使用默认文件名参数
     * @param url post请求地址
     * @param headers 请求头
     * @param formData 请求参数formData 方式
     * @param file 上传文件
     * @param fileParamName 上传文件的参数名
     * @return JSONObject 对象，包含headers code 和data,异常时返回null
     */
    public static JSONObject sendPostFormDataGetObj(String url,Map<String,Object> headers, Map<String,Object> formData,File file,String fileParamName){
        if (file == null){
            throw new RuntimeException("file not is null!");
        }
        Map<String,File> files = new HashMap<>();
        files.put(fileParamName,file);
        return sendFormDataFilesGetObj(url,headers,formData,files);
    }
    /**
     * post formData 方式发送参数及文件，或者单独文件并使用默认文件名参数
     * @param url post请求地址
     * @param headers 请求头
     * @param formData 请求参数formData 方式
     * @param files 文件，可以为多了，或者单个
     * @return JSONObject 对象，包含headers code 和data,异常时返回null
     */
    public static JSONObject sendPostFormDataGetObj(String url,Map<String,Object> headers, Map<String,Object> formData,Map<String,File> files){
        return sendFormDataFilesGetObj(url,headers,formData,files);
    }

    /*************************post application/json 方式请求接口返回字符串***************/
    /**
     * post 不带参数或者参数封装在url上
     * @param url 请求url
     * @param headers 请求头,可以为空
     * @return
     */
    public static String sendPostRequestOfJson(String url,Map<String,Object> headers){
        return sendPostRequest(url,headers,null);
    }
    /**
     * post application/json 方式请求
     * @param url 请求url
     * @param headers 请求头
     * @param jsonParams json 格式参数
     * @return
     */
    public static String sendPostRequestOfJson(String url,Map<String,Object> headers,String jsonParams){
        return sendPostRequest(url,headers,jsonParams);
    }

    /**
     * post application/json 方式请求
     * @param url 请求url
     * @param headers 请求头
     * @param jsonParams map
     * @return
     */
    public static String sendPostRequestOfJson(String url,Map<String,Object> headers,Map<String,Object> jsonParams){
        return sendPostRequest(url,headers,JSON.toJSONString(jsonParams));
    }

    /**
     * post 不带参数或者参数封装在url上
     * @param url 请求地址
     * @param headers 请求头
     * @return JSONObject 对象，包含headers code 和data,异常时返回null
     */
    public static JSONObject sendPostRequestOfJsonGetObj(String url,Map<String,Object> headers){
        return sendPostRequestGetObj(url,headers,null);
    }

    /**
     * post application/json 方式请求
     * @param url 请求地址
     * @param headers 请求头
     * @param jsonParams JSON 格式参数
     * @return JSONObject 对象，包含headers code 和data,异常时返回null
     */
    public static JSONObject sendPostRequestOfJsonGetObj(String url,Map<String,Object> headers,String jsonParams){
        return sendPostRequestGetObj(url,headers,jsonParams);
    }

    /**
     * post application/json 方式请求
     * @param url 请求地址
     * @param headers 请求头
     * @param jsonParams JSON 格式参数
     * @return JSONObject 对象，包含headers code 和data,异常时返回null
     */
    public static JSONObject sendPostRequestOfJsonGetObj(String url,Map<String,Object> headers,Map<String,Object> jsonParams){
        return sendPostRequestGetObj(url,headers,JSON.toJSONString(jsonParams));
    }


    /**
     * get 请求 参数直接放在地址上，或者通过map封装到地址上
     * @param url
     * @param params
     * @return
     */
    private static String sendGetRequest(String url,Map<String,Object> params){
        String resultStr = null;
        CloseableHttpClient client = HttpClients.createDefault();
        if (params != null && !params.isEmpty()){
            url+="?" + asUrlParams(params);
        }
        HttpGet httpGet = new HttpGet(url);
        try {
            CloseableHttpResponse response = client.execute(httpGet);
            HttpEntity entity = response.getEntity();
            resultStr = EntityUtils.toString(entity, "UTF-8");
            response.close();
            client.close();
        }catch (Exception e){
            logger.error("send get request failed! {}" + url);
            e.printStackTrace();
        }
        return resultStr;
    }

    /**
     * get 请求 参数直接放在地址上，或者通过map封装到地址上
     * @param url 请求地址
     * @param params url
     * @return 成功返回对象，失败或者异常返回null
     */
    private static JSONObject sendGetRequestGetObj(String url,Map<String,Object> params){
        JSONObject result = new JSONObject();
        CloseableHttpClient client = HttpClients.createDefault();
        if (params != null && !params.isEmpty()){
            url+="?" + asUrlParams(params);
        }
        HttpGet httpGet = new HttpGet(url);
        try {
            CloseableHttpResponse response = client.execute(httpGet);
            result.put("code",response.getStatusLine().getStatusCode());
            result.put("headers",getResHeader(response));
            result.put("data",analysisResContent(response));
            response.close();
            client.close();
        }catch (Exception e){
            logger.error("send get request failed! {}" + url);
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 发送post 请求
     * @param url 请求地址
     * @param headers 请求头
     * @param jsonParams json 参数
     * @return
     */
    private static String sendPostRequest(String url,Map<String,Object> headers,String jsonParams){
        String resultStr = null;
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        //设置请求头参数
        setHeaders(httpPost,headers);
        try{
            if (StringUtils.isNotBlank(jsonParams)){
                httpPost.addHeader("Content-Type", "application/json");
                httpPost.setEntity(new StringEntity(jsonParams,"UTF-8"));
            }
            CloseableHttpResponse response = client.execute(httpPost);
            HttpEntity entity = response.getEntity();
            resultStr = EntityUtils.toString(entity, "UTF-8");
            response.close();
            client.close();
        }catch (Exception e){
            logger.error("send request body json failed!");
            e.printStackTrace();
        }
        return resultStr;
    }

    /**
     * 发送post 请求 返回json对象 可以拿到header头部的返回结果
     * @param url 请求地址
     * @param headers 请求头
     * @param jsonParams json 参数
     * @return
     */
    private static JSONObject sendPostRequestGetObj(String url,Map<String,Object> headers,String jsonParams){
        JSONObject result = new JSONObject();
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        //设置请求头参数
        setHeaders(httpPost,headers);
        try{
            if (StringUtils.isNotBlank(jsonParams)){
                httpPost.addHeader("Content-Type", "application/json");
                httpPost.setEntity(new StringEntity(jsonParams,"UTF-8"));
            }
            CloseableHttpResponse response = client.execute(httpPost);
            HttpEntity entity = response.getEntity();
            result.put("code",response.getStatusLine().getStatusCode());
            result.put("headers",getResHeader(response));
            result.put("data",analysisResContent(response));
            response.close();
            client.close();
        }catch (Exception e){
            logger.error("send request body json failed!");
            e.printStackTrace();
        }
        return result;
    }

    /**
     * post formData 方式发送参数及文件，或者单独文件
     * @param url 地址
     * @param formData 参数
     * @param files 文件对象
     * @return
     */
    private static String sendFormDataFiles(String url,Map<String,Object> headers, Map<String,Object> formData,Map<String,File> files){
        String resultStr = null;
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        //设置header
        setHeaders(httpPost,headers);
        //设置请求参数和上传文件
        setFormDataOrFile(builder,formData,files);
        HttpEntity multipart = builder.build();
        CloseableHttpResponse resp;
        try {
            httpPost.setEntity(multipart);
            resp = client.execute(httpPost);
            resultStr = analysisResContent(resp);
            resp.close();
            client.close();
        } catch (IOException e) {
            logger.error("post 请求 {} 失败!" + url);
            e.printStackTrace();
        }
        return resultStr;
    }

    /**
     * post formData 方式发送参数及文件，或者单独文件 返回json对象 可以拿到header头部的返回结果
     * @param url 地址
     * @param formData 参数
     * @param files 文件对象
     * @return
     */
    private static JSONObject sendFormDataFilesGetObj(String url, Map<String,Object> headers, Map<String,Object> formData, Map<String,File> files){
        JSONObject result = new JSONObject();
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        //设置header
        setHeaders(httpPost,headers);
        //设置请求参数和上传文件
        setFormDataOrFile(builder,formData,files);
        HttpEntity multipart = builder.build();
        CloseableHttpResponse resp;
        try {
            httpPost.setEntity(multipart);
            resp = client.execute(httpPost);
            result.put("code",resp.getStatusLine().getStatusCode());
            result.put("headers",getResHeader(resp));
            result.put("data",analysisResContent(resp));
            resp.close();
            client.close();
        } catch (IOException e) {
            logger.error("post 请求 {} 失败!" + url);
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 将map的值转换为url形式
     * @param params
     * @return
     */
    private static String asUrlParams(Map<String, Object> params){
        // TODO 如果要编码的话自己加下编码逻辑
        return Joiner.on("&")
                // 用指定符号代替空值,key 或者value 为null都会被替换
                .useForNull("")
                .withKeyValueSeparator("=")
                .join(params);
    }


    //设置请求头参数
    private static void setHeaders(HttpPost httpPost,Map<String,Object> headers){
        //设置请求头参数
        if (headers != null && !headers.isEmpty()){
            for (Map.Entry<String,Object> entry : headers.entrySet()){
                httpPost.addHeader(entry.getKey(), String.valueOf(entry.getValue()));
            }
        }
    }

    //设置formData 以及文件
    private static void setFormDataOrFile(MultipartEntityBuilder builder,Map<String,Object> formData,Map<String,File> files){
        //设置上传文件
        if (files != null && !files.isEmpty()){
            for(Map.Entry<String,File> entry : files.entrySet()){
                FileBody fb = new FileBody(entry.getValue(), ContentType.DEFAULT_BINARY);
                builder.addPart(entry.getKey(), fb);
            }
        }
        //设置formData请求参数
        if (formData != null && !formData.isEmpty()){
            for (Map.Entry<String,Object> entry : formData.entrySet()){
                builder.addTextBody(entry.getKey(),entry.getValue() == null ? null : entry.getValue().toString());
            }
        }
    }

    /**
     * 获取响应头header数据
     * @param response
     * @return
     */
    private static JSONObject getResHeader(HttpResponse response){
        JSONObject objRes = new JSONObject();
        Header[] resHeaders = response.getAllHeaders();
        for (Header header : resHeaders) {
            objRes.put(header.getName(),header.getValue());
        }
        logger.info("获取头部数据：" + objRes.toJSONString());
        return objRes;
    }

    /**
     * 封装请求结果
     * @param response
     * @return
     */
    private static String analysisResContent(HttpResponse response){
        String resContent = null;
        try {
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                HttpEntity responseEntity = response.getEntity();
                resContent = EntityUtils.toString(responseEntity, "utf-8");
            }
        }catch (IOException e){
            logger.error("convert result failed!");
            e.printStackTrace();
        }
        return resContent;
    }
}
